/*******************************************************************************
* This file is part of ecco.
*
* ecco is distributed under the terms of the GNU Lesser General Public License (LGPL), Version 3.0.
*
* Copyright 2011-2014, The University of Manchester
*
* ecco is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* ecco is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with ecco.
* If not, see http://www.gnu.org/licenses/.
******************************************************************************/
package uk.ac.manchester.cs.diff.axiom;
import java.io.StringWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.semanticweb.owl.explanation.api.Explanation;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.AxiomType;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLEquivalentClassesAxiom;
import org.semanticweb.owlapi.model.OWLObject;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.util.ShortFormProvider;
import org.semanticweb.owlapi.util.SimpleShortFormProvider;
import uk.ac.manchester.cs.diff.axiom.change.CategorisedChange;
import uk.ac.manchester.cs.diff.axiom.change.CategorisedEffectualAddition;
import uk.ac.manchester.cs.diff.axiom.change.CategorisedEffectualChange;
import uk.ac.manchester.cs.diff.axiom.change.CategorisedEffectualRemoval;
import uk.ac.manchester.cs.diff.axiom.change.CategorisedIneffectualAddition;
import uk.ac.manchester.cs.diff.axiom.change.CategorisedIneffectualRemoval;
import uk.ac.manchester.cs.diff.axiom.change.CategorisedEffectualAddition.EffectualAdditionCategory;
import uk.ac.manchester.cs.diff.axiom.change.CategorisedEffectualRemoval.EffectualRemovalCategory;
import uk.ac.manchester.cs.diff.axiom.change.CategorisedIneffectualAddition.IneffectualAdditionCategory;
import uk.ac.manchester.cs.diff.axiom.change.CategorisedIneffectualRemoval.IneffectualRemovalCategory;
import uk.ac.manchester.cs.diff.axiom.changeset.CategorisedChangeSet;
import uk.ac.manchester.cs.diff.axiom.changeset.LogicalChangeSet;
import uk.ac.manchester.cs.diff.axiom.changeset.StructuralChangeSet;
import uk.ac.manchester.cs.diff.justifications.JustificationFinder;
import uk.ac.manchester.cs.diff.output.csv.CSVAxiomDiffReport;
import uk.ac.manchester.cs.diff.output.xml.XMLAxiomDiffReport;
import uk.ac.manchester.cs.diff.utils.ProgressMonitor;
import uk.ac.manchester.cs.diff.utils.ReasonerLoader;
import uk.ac.manchester.cs.owl.owlapi.mansyntaxrenderer.ManchesterOWLSyntaxObjectRenderer;
import uk.ac.manchester.cs.owlapi.modularity.ModuleType;
import uk.ac.manchester.cs.owlapi.modularity.SyntacticLocalityModuleExtractor;
import com.clarkparsia.owlapi.modularity.locality.LocalityClass;
import com.clarkparsia.owlapi.modularity.locality.SyntacticLocalityEvaluator;
/**
* @author Rafael S. Goncalves <br>
* Information Management Group (IMG) <br>
* School of Computer Science <br>
* University of Manchester <br>
*/
public class CategoricalDiff implements AxiomDiff {
private OWLOntology ont1, ont2;
private OWLOntologyManager man;
private OWLDataFactory df;
private StructuralChangeSet structuralChangeSet;
private CategorisedChangeSet categorisedChangeSet;
private LogicalChangeSet logicalChangeSet;
private OWLReasoner ont1reasoner, ont2reasoner, emptyOntReasoner;
private Set<OWLAxiom> sharedAxioms;
private double diffTime, eaTime, erTime, iaTime, irTime, iaJustTime, irJustTime;
private int nrJusts;
private boolean verbose;
private static ShortFormProvider p = new SimpleShortFormProvider();
/**
* Constructor
* @param ont1 Ontology 1
* @param ont2 Ontology 2
* @param nrJusts Number of justifications to be computed
* @param verbose Verbose mode
*/
public CategoricalDiff(OWLOntology ont1, OWLOntology ont2, int nrJusts, boolean verbose) {
this.ont1 = ont1;
this.ont2 = ont2;
this.nrJusts = nrJusts;
this.verbose = verbose;
man = OWLManager.createOWLOntologyManager();
df = man.getOWLDataFactory();
emptyOntReasoner = createEmptyOntologyReasoner();
}
/**
* Constructor that takes a logical change set
* @param ont1 Ontology 1
* @param ont2 Ontology 2
* @param logicalChangeSet Logical change set
* @param nrJusts Number of justifications to be computed
* @param verbose Verbose mode
*/
public CategoricalDiff(OWLOntology ont1, OWLOntology ont2, LogicalChangeSet logicalChangeSet, int nrJusts, boolean verbose) {
this.ont1 = ont1;
this.ont2 = ont2;
this.logicalChangeSet = logicalChangeSet;
this.nrJusts = nrJusts;
this.verbose = verbose;
man = OWLManager.createOWLOntologyManager();
df = man.getOWLDataFactory();
emptyOntReasoner = createEmptyOntologyReasoner();
}
/**
* Get categorised logical changes between ontologies
* @return Categorised logical change set
*/
@SuppressWarnings("deprecation")
public CategorisedChangeSet getDiff() {
if(categorisedChangeSet != null) return categorisedChangeSet;
if(verbose) System.out.println("\nComputing axiom diff...");
long start = System.currentTimeMillis();
ont1reasoner = new ReasonerLoader(ont1, false).createReasoner(false);
ont2reasoner = new ReasonerLoader(ont2, false).createReasoner(false);
if(logicalChangeSet == null) {
LogicalDiffConcurrent lDiff = new LogicalDiffConcurrent(ont1, ont2, verbose);
logicalChangeSet = lDiff.getDiff(ont1reasoner, ont2reasoner);
structuralChangeSet = lDiff.getStructuralChangeSet();
sharedAxioms = structuralChangeSet.getShared();
if(logicalChangeSet == null) return null;
}
Set<OWLAxiom> ea = logicalChangeSet.getEffectualAdditionAxioms();
Set<OWLAxiom> er = logicalChangeSet.getEffectualRemovalAxioms();
Set<OWLAxiom> ia = logicalChangeSet.getIneffectualAdditionAxioms();
Set<OWLAxiom> ir = logicalChangeSet.getIneffectualRemovalAxioms();
if(verbose) System.out.print(" Computing change categorisation... ");
long start2 = System.currentTimeMillis();
Set<CategorisedEffectualAddition> effAdds = categoriseEffectualAdditions(ea, er, ir);
Set<CategorisedEffectualRemoval> effRems = categoriseEffectualRemovals(er, ea, ia);
Set<CategorisedIneffectualAddition> ineffAdds = categoriseIneffectualAdditions(ia, er, ir);
Set<CategorisedIneffectualRemoval> ineffRems = categoriseIneffectualRemovals(ir, ea, ia);
long end = System.currentTimeMillis();
if(verbose) System.out.print("done (" + (end-start2)/1000.0 + " secs)");
diffTime = (end-start)/1000.0;
categorisedChangeSet = new CategorisedChangeSet(effAdds, ineffAdds, effRems, ineffRems, sharedAxioms, logicalChangeSet);
categorisedChangeSet.setDiffTime(diffTime);
categorisedChangeSet.setEffectualAdditionCategorisationTime(eaTime);
categorisedChangeSet.setEffectualRemovalCategorisationTime(erTime);
categorisedChangeSet.setIneffectualRemovalCategorisationTime(irTime);
categorisedChangeSet.setIneffectualAdditionCategorisationTime(iaTime);
categorisedChangeSet.setIneffectualAdditionJustificationFindingTime(iaJustTime);
categorisedChangeSet.setIneffectualRemovalJustificationFindingTime(irJustTime);
if(verbose) System.out.println("\nfinished axiom diff (" + diffTime + " seconds)");
if(verbose) printDiff();
return categorisedChangeSet;
}
/**
* Categorise effectual additions
* @param ea Effectual additions
* @param er Effectual removals
* @param ir Ineffectual removals
* @return Set of categorised effectual additions
*/
@SuppressWarnings("unchecked")
private Set<CategorisedEffectualAddition> categoriseEffectualAdditions(Set<OWLAxiom> ea, Set<OWLAxiom> er, Set<OWLAxiom> ir) {
if(verbose) System.out.println("\n Categorising effectual additions... ");
Set<CategorisedEffectualAddition> effAdds = new HashSet<CategorisedEffectualAddition>();
if(!ea.isEmpty())
try { effAdds = (Set<CategorisedEffectualAddition>) categoriseEffectualChanges(true, ea, ont1, er, ir); }
catch (OWLOntologyCreationException e) { e.printStackTrace(); }
else
if(verbose) System.out.println(" done (no effectual additions)");
return effAdds;
}
/**
* Categorise effectual removals
* @param er Effectual removals
* @param ea Effectual additions
* @param ia Ineffectual additions
* @return Set of categorised effectual removals
*/
@SuppressWarnings("unchecked")
private Set<CategorisedEffectualRemoval> categoriseEffectualRemovals(Set<OWLAxiom> er, Set<OWLAxiom> ea, Set<OWLAxiom> ia) {
if(verbose) System.out.println(" Categorising effectual removals... ");
Set<CategorisedEffectualRemoval> effRems = new HashSet<CategorisedEffectualRemoval>();
if(!er.isEmpty())
try { effRems = (Set<CategorisedEffectualRemoval>) categoriseEffectualChanges(false, er, ont2, ea, ia); }
catch (OWLOntologyCreationException e) { e.printStackTrace(); }
else
if(verbose) System.out.println(" done (no effectual removals)");
return effRems;
}
/**
* Categorise ineffectual additions
* @param ia Ineffectual additions
* @param er Effectual removals
* @param ir Ineffectual removals
* @return Set of categorised ineffectual additions
*/
@SuppressWarnings("unchecked")
private Set<CategorisedIneffectualAddition> categoriseIneffectualAdditions(Set<OWLAxiom> ia, Set<OWLAxiom> er, Set<OWLAxiom> ir) {
if(verbose) System.out.println(" Categorising ineffectual additions... ");
Set<CategorisedIneffectualAddition> ineffAdds = new HashSet<CategorisedIneffectualAddition>();
if(!ia.isEmpty())
ineffAdds = (Set<CategorisedIneffectualAddition>) categoriseIneffectualChanges("rhs", ia, er, ir, ont1, ont2reasoner);
else
if(verbose) System.out.println(" done (no ineffectual additions)");
return ineffAdds;
}
/**
* Categorise ineffectual removals
* @param ir Ineffectual removals
* @param ea Effectual additions
* @param ia Ineffectual additions
* @return Set of categorised ineffectual removals
*/
@SuppressWarnings("unchecked")
private Set<CategorisedIneffectualRemoval> categoriseIneffectualRemovals(Set<OWLAxiom> ir, Set<OWLAxiom> ea, Set<OWLAxiom> ia) {
if(verbose) System.out.println(" Categorising ineffectual removals... ");
Set<CategorisedIneffectualRemoval> ineffRems = new HashSet<CategorisedIneffectualRemoval>();
if(!ir.isEmpty())
ineffRems = (Set<CategorisedIneffectualRemoval>) categoriseIneffectualChanges("lhs", ir, ea, ia, ont2, ont1reasoner);
else
if(verbose) System.out.println(" done (no ineffectual removals)");
return ineffRems;
}
/**
* Categorise effectual changes
* @param effAdds true if checking effectual additions, false for removals
* @param axioms Set of changes to categorise
* @param ont if(effAdds) then ont1, else ont2
* @param effectual if(effAdds) then Set of effectual removals, else Set of effectual additions
* @param ineffectual if(effAdds) then Set of ineffectual removals, else Set of ineffectual additions
* @return Set of categorised changes
* @throws OWLOntologyCreationException Ontology creation exception
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private Set<? extends CategorisedEffectualChange> categoriseEffectualChanges(boolean effAdds, Set<OWLAxiom> axioms, OWLOntology ont,
Set<OWLAxiom> effectual, Set<OWLAxiom> ineffectual) throws OWLOntologyCreationException {
long start = System.currentTimeMillis();
Set result = null;
if(effAdds) result = new HashSet<CategorisedEffectualAddition>();
else result = new HashSet<CategorisedEffectualRemoval>();
ProgressMonitor progress = new ProgressMonitor(axioms);
int status = 0;
Set<OWLEntity> ontSig = ont.getSignature();
OWLOntologyManager man = ont.getOWLOntologyManager();
SyntacticLocalityModuleExtractor modExtractorEff = new SyntacticLocalityModuleExtractor(man, ont, ModuleType.STAR);
// FaCTPlusPlusReasoner modExtractorEff = new FaCTPlusPlusReasoner(ont, new SimpleConfiguration(), BufferingMode.NON_BUFFERING);
SyntacticLocalityEvaluator eval = new SyntacticLocalityEvaluator(LocalityClass.BOTTOM_BOTTOM);
// Loop effectual changes
for(OWLAxiom ax : axioms) {
if(ax.isLogicalAxiom()) {
boolean stOrWkAx = false, newOrRetiredAx = false, modEquiv = false;
Set<OWLEntity> sig = ax.getSignature();
Set<OWLEntity> newTerms = new HashSet<OWLEntity>(); // New terms in the axiom
for(OWLEntity e : sig) {
if(!ontSig.contains(e) && !e.isTopEntity() && !e.isBottomEntity())
newTerms.add(e);
}
// Check New or Retired Descriptions
CategorisedChange nd = checkNewOrRetiredDescription(effAdds, man, ax, newTerms, eval);
if(nd != null) {
newOrRetiredAx = true;
result.add(nd);
}
// Check Strengthenings or Weakenings
if(!newOrRetiredAx) {
CategorisedChange st = checkStrengtheningOrWeakening(effAdds, man, ax, ineffectual, newTerms);
if(st != null) {
stOrWkAx = true;
result.add(st);
}
}
// Check Modified Definitions
if(!newOrRetiredAx && !stOrWkAx) {
if(ax.isOfType(AxiomType.EQUIVALENT_CLASSES)) {
CategorisedChange me = checkModifiedDefinitions(effAdds, man, ax, modExtractorEff, newTerms);
if(me != null) {
modEquiv = true;
result.add(me);
}
}
}
// Pure Alterations
if(!newOrRetiredAx && !stOrWkAx && !modEquiv) {
if(effAdds) {
if(newTerms.isEmpty())
result.add(new CategorisedEffectualAddition(ax, EffectualAdditionCategory.PUREADDITION, new HashSet<OWLAxiom>(), newTerms));
else
result.add(new CategorisedEffectualAddition(ax, EffectualAdditionCategory.PUREADDITIONNT, new HashSet<OWLAxiom>(), newTerms));
}
else {
if(newTerms.isEmpty())
result.add(new CategorisedEffectualRemoval(ax, EffectualRemovalCategory.PUREREMOVAL, new HashSet<OWLAxiom>(), newTerms));
else
result.add(new CategorisedEffectualRemoval(ax, EffectualRemovalCategory.PUREREMOVALRT, new HashSet<OWLAxiom>(), newTerms));
}
}
} // end if logical axiom
int p = progress.incrementProgress();
if(p > status && verbose) {
System.out.print("\r\t" + p + "%");
status = p;
}
} // end for loop
modExtractorEff = null; progress = null;
long end = System.currentTimeMillis();
double total = (end-start)/1000.0;
if(verbose) System.out.println("\n done (" + total + " secs)");
if(effAdds) eaTime = total;
else erTime = total;
return result;
}
/**
* Check whether a given change is a strengthening (or weakening)
* @param effAdds true if checking additions, false if checking removals
* @param man OWL ontology manager
* @param ax OWL axiom to be checked
* @param searchSpace Set of axioms in the search space
* @param newTerms Set of new terms used in this axiom
* @return Strengthening or Weakening-type change, or null if not a strengthening or weakening
*/
private CategorisedChange checkStrengtheningOrWeakening(boolean effAdds, OWLOntologyManager man, OWLAxiom ax,
Set<OWLAxiom> searchSpace, Set<OWLEntity> newTerms) {
CategorisedChange change = null;
OWLOntology axOnt = createOntology(ax);
OWLReasoner reasoner = new ReasonerLoader(axOnt).createReasoner(false);
Set<OWLAxiom> stAlignments = new HashSet<OWLAxiom>();
for(OWLAxiom axiom : searchSpace) {
if(axiom.isLogicalAxiom() && signatureOverlaps(ax, axiom)) {
if(reasoner.isEntailed(axiom) && !emptyOntReasoner.isEntailed(axiom)) {
stAlignments.add(axiom);
break;
}
}
}
if(!stAlignments.isEmpty()) {
if(newTerms.isEmpty()) {
if(effAdds)
change = new CategorisedEffectualAddition(ax, EffectualAdditionCategory.STRENGTHENING, stAlignments, newTerms);
else
change = new CategorisedEffectualRemoval(ax, EffectualRemovalCategory.WEAKENING, stAlignments, newTerms);
}
else {
if(effAdds)
change = new CategorisedEffectualAddition(ax, EffectualAdditionCategory.STRENGTHENINGNT, stAlignments, newTerms);
else
change = new CategorisedEffectualRemoval(ax, EffectualRemovalCategory.WEAKENINGRT, stAlignments, newTerms);
}
}
cleanUp(reasoner); cleanUp(axOnt);
return change;
}
/**
* Check whether a given change is a new or retired description
* @param effAdds true if checking additions, false if checking removals
* @param man OWL ontology manager
* @param ax OWL axiom to be checked
* @param newTerms Set of new terms used in this axiom
* @param eval Syntactic locality evaluator
* @return New or Retired Description-type change, or null if not one
* @throws OWLOntologyCreationException Ontology creation exception
*/
private CategorisedChange checkNewOrRetiredDescription(boolean effAdds, OWLOntologyManager man, OWLAxiom ax,
Set<OWLEntity> newTerms, SyntacticLocalityEvaluator eval) throws OWLOntologyCreationException {
CategorisedChange change = null;
if(!newTerms.isEmpty()) {
boolean atomicLhs = true;
if(ax instanceof OWLSubClassOfAxiom) {
OWLClassExpression c = ((OWLSubClassOfAxiom)ax).getSubClass();
if(c.isAnonymous() || !newTerms.contains(c))
atomicLhs = false;
}
else if(ax instanceof OWLEquivalentClassesAxiom)
atomicLhs = false;
if(atomicLhs && !(eval.isLocal(ax, newTerms))) {
if(effAdds)
change = new CategorisedEffectualAddition(ax, EffectualAdditionCategory.NEWDESCRIPTION, new HashSet<OWLAxiom>(), newTerms);
else
change = new CategorisedEffectualRemoval(ax, EffectualRemovalCategory.RETIREDDESCRIPTION, new HashSet<OWLAxiom>(), newTerms);
}
}
return change;
}
/**
* Check if given change is a modified equivalence
* @param effAdds true if checking additions, false if checking removals
* @param man OWL ontology manager
* @param ax OWL axiom to be checked
* @param modExtractor Star module extractor
* @param newTerms Set of new terms used in this axiom
* @return Modified definition-type change, or null if not one
* @throws OWLOntologyCreationException Ontology creation exception
*/
private CategorisedChange checkModifiedDefinitions(boolean effAdds, OWLOntologyManager man, OWLAxiom ax,
SyntacticLocalityModuleExtractor modExtractor, Set<OWLEntity> newTerms) throws OWLOntologyCreationException {
CategorisedChange change = null;
Set<OWLAxiom> alignment = new HashSet<OWLAxiom>();
OWLEquivalentClassesAxiom equiv = (OWLEquivalentClassesAxiom) ax;
Set<OWLSubClassOfAxiom> subs = equiv.asOWLSubClassOfAxioms();
OWLSubClassOfAxiom sub1 = (OWLSubClassOfAxiom) subs.toArray()[0];
OWLClassExpression lhs = sub1.getSubClass();
OWLClassExpression rhs = sub1.getSuperClass();
Set<OWLAxiom> mod = modExtractor.extract(ax.getSignature());
loop:
for(OWLAxiom a : mod) {
if(a.isOfType(AxiomType.EQUIVALENT_CLASSES)) {
OWLEquivalentClassesAxiom y = (OWLEquivalentClassesAxiom) a;
Set<OWLSubClassOfAxiom> subs2 = y.asOWLSubClassOfAxioms();
for(OWLSubClassOfAxiom sub : subs2) {
boolean isEquiv = false;
if(sub.getSubClass().equals(lhs)) {
if(emptyOntReasoner.isEntailed(df.getOWLSubClassOfAxiom(sub.getSuperClass(), rhs)) ||
emptyOntReasoner.isEntailed(df.getOWLSubClassOfAxiom(rhs, sub.getSuperClass())))
isEquiv = true;
}
else if(sub.getSubClass().equals(rhs)) {
if(emptyOntReasoner.isEntailed(df.getOWLSubClassOfAxiom(sub.getSuperClass(), lhs)) ||
emptyOntReasoner.isEntailed(df.getOWLSubClassOfAxiom(lhs, sub.getSuperClass())))
isEquiv = true;
}
else if(sub.getSuperClass().equals(lhs)) {
if(emptyOntReasoner.isEntailed(df.getOWLSubClassOfAxiom(sub.getSubClass(), rhs)) ||
emptyOntReasoner.isEntailed(df.getOWLSubClassOfAxiom(rhs, sub.getSubClass())))
isEquiv = true;
}
else if(sub.getSuperClass().equals(lhs)) {
if(emptyOntReasoner.isEntailed(df.getOWLSubClassOfAxiom(sub.getSubClass(), lhs)) ||
emptyOntReasoner.isEntailed(df.getOWLSubClassOfAxiom(lhs, sub.getSubClass())))
isEquiv = true;
}
if(isEquiv) {
alignment.add(a);
break loop;
}
}
}
}
if(!alignment.isEmpty()) {
if(effAdds) {
if(newTerms.isEmpty())
change = new CategorisedEffectualAddition(ax, EffectualAdditionCategory.MODIFIEDDEFINITION, alignment, newTerms);
else
change = new CategorisedEffectualAddition(ax, EffectualAdditionCategory.MODIFIEDDEFINITIONNT, alignment, newTerms);
}
else {
if(newTerms.isEmpty())
change = new CategorisedEffectualRemoval(ax, EffectualRemovalCategory.MODIFIEDDEFINITION, alignment, newTerms);
else
change = new CategorisedEffectualRemoval(ax, EffectualRemovalCategory.MODIFIEDDEFINITIONRT, alignment, newTerms);
}
}
return change;
}
/**
* Categorise the given set of ineffectual changes
* @param desc "rhs" if categorising additions, "lhs" if removals
* @param axioms Axiom changes to categorise
* @param effectual Set of effectual changes of the opposite ontology
* @param ineffectual Set of ineffectual changes of the opposite ontology
* @param ont Opposite ontology
* @param src_reasoner Reasoner instance for source ontology
* @return Set of categorised ineffectual changes
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private Set<? extends CategorisedChange> categoriseIneffectualChanges(String desc, Set<OWLAxiom> axioms, Set<OWLAxiom> effectual,
Set<OWLAxiom> ineffectual, OWLOntology ont, OWLReasoner src_reasoner) {
Set result = null;
if(desc.equals("rhs")) result = new HashSet<CategorisedIneffectualAddition>();
else result = new HashSet<CategorisedIneffectualRemoval>();
long start = System.currentTimeMillis();
if(verbose) System.out.print("\tComputing justifications... ");
JustificationFinder just = new JustificationFinder(ont, nrJusts);
Map<OWLAxiom,Set<Explanation<OWLAxiom>>> exps = just.getJustifications(axioms);
double justTime = (System.currentTimeMillis()-start)/1000.0;
if(verbose) System.out.println("done (" + justTime + " secs)");
ProgressMonitor progress = new ProgressMonitor(exps.keySet());
int status = 0;
for(Set<Explanation<OWLAxiom>> expsSet : exps.values()) {
try {
if(!expsSet.isEmpty())
result.add(categoriseIneffectualChange(desc, expsSet, effectual, ineffectual, ont, just, src_reasoner));
} catch (OWLOntologyCreationException e) {
e.printStackTrace();
}
int p = progress.incrementProgress();
if(p > status && verbose) {
System.out.print("\r\t" + p + "%");
status = p;
}
}
long end = System.currentTimeMillis();
double total = (end-start)/1000.0;
if(desc.equals("rhs")) {
iaTime = total;
iaJustTime = justTime;
}
else {
irTime = total;
irJustTime = justTime;
}
if(verbose) System.out.println("\n done (" + total + " secs)");
cleanUp(src_reasoner); cleanUp(exps); just = null;
return result;
}
/**
* Categorise the given ineffectual change
* @param desc "rhs" if categorising additions, "lhs" if removals
* @param exps Set of justifications for this change
* @param effectual Effectual changes
* @param ineffectual Ineffectual changes
* @param ont Target ontology
* @param just Justification generator interface
* @param src_reasoner Reasoner instance with the source ontology loaded
* @return Categorised change
* @throws OWLOntologyCreationException Ontology creation exception
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private CategorisedChange categoriseIneffectualChange(String desc, Set<Explanation<OWLAxiom>> exps, Set<OWLAxiom> effectual,
Set<OWLAxiom> ineffectual, OWLOntology ont, JustificationFinder just, OWLReasoner src_reasoner)
throws OWLOntologyCreationException {
boolean entailmentAssigned = false;
OWLAxiom entailment = null;
HashMap justMap = null;
if(desc.equals("rhs")) justMap = new HashMap<Explanation<OWLAxiom>,Set<IneffectualAdditionCategory>>();
else justMap = new HashMap<Explanation<OWLAxiom>,Set<IneffectualRemovalCategory>>();
OWLOntology entOnt = null; OWLReasoner reasoner = null;
for(Explanation<OWLAxiom> explanation : exps) {
boolean prospRedundantNovelAx = false, rewrittenAx = false, redundancyAx = false, prospRedundantAx = false;
if(!entailmentAssigned) {
entailment = explanation.getEntailment();
entailmentAssigned = true;
entOnt = createOntology(entailment);
reasoner = new ReasonerLoader(entOnt).createReasoner(false);
}
int entailedAxs = 0, shared = 0;
for(OWLAxiom ax : explanation.getAxioms()) {
if(ax.isOfType(AxiomType.TBoxAxiomTypes)) {
// Rewrite: If the axiom entails the justification, the axiom is rewritten
if(signatureOverlaps(entailment, ax) && reasoner.isEntailed(ax)) entailedAxs ++;
// Prospective novel redundancy: If the justification intersects with the effectual additions or removals
if(effectual.contains(ax)) prospRedundantNovelAx = true;
// Redundant: If the justification intersects with the intersection
}
if(sharedAxioms.contains(ax)) shared++;
if(sharedAxioms.contains(ax) || ineffectual.contains(ax)) prospRedundantAx = true;
}
// Rewritten axiom
if(entailedAxs == explanation.getSize()) {
rewrittenAx = true;
if(shared > 0)
updateJustificationMap(desc, justMap, explanation, "prewrite"); // partial rewrite
else
updateJustificationMap(desc, justMap, explanation, "rewrite"); // complete rewrite
}
// Redundant axiom
if(shared == explanation.getSize() && !rewrittenAx && !prospRedundantNovelAx) {
updateJustificationMap(desc, justMap, explanation, "redundant");
redundancyAx = true;
}
// Prospective reshuffle redundancy
if(prospRedundantAx && !rewrittenAx && !redundancyAx && !prospRedundantNovelAx)
updateJustificationMap(desc, justMap, explanation, "reshuffle");
// Prospective new redundancy
if(prospRedundantNovelAx && !rewrittenAx && !redundancyAx)
updateJustificationMap(desc, justMap, explanation, "new");
} // end for each explanation
cleanUp(reasoner);
cleanUp(entOnt);
if(desc.equals("lhs")) return new CategorisedIneffectualRemoval(entailment, justMap);
else return new CategorisedIneffectualAddition(entailment, justMap);
}
/**
* Update ineffectual change justification map with a new category
* @param desc "rhs" if checking additions, "lhs" for removals
* @param justMap Justification map
* @param exp Justification
* @param cat New category
*/
@SuppressWarnings("rawtypes")
private void updateJustificationMap(String desc, HashMap justMap, Explanation<OWLAxiom> exp, String cat) {
if(desc.equals("rhs"))
updateJustificationMapWithAddition(justMap, exp, cat);
else
updateJustificationMapWithRemoval(justMap, exp, cat);
}
/**
* Update ineffectual addition justification map with a new category
* @param justMap Justification map
* @param exp Justification
* @param cat New category
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private void updateJustificationMapWithAddition(HashMap justMap, Explanation<OWLAxiom> exp, String cat) {
IneffectualAdditionCategory category = null;
if(cat.equalsIgnoreCase("redundant"))
category = IneffectualAdditionCategory.REDUNDANCY;
else if(cat.equalsIgnoreCase("rewrite"))
category = IneffectualAdditionCategory.REWRITE;
else if(cat.equalsIgnoreCase("prewrite"))
category = IneffectualAdditionCategory.PREWRITE;
else if(cat.equalsIgnoreCase("reshuffle"))
category = IneffectualAdditionCategory.RESHUFFLEREDUNDANCY;
else if(cat.equalsIgnoreCase("new"))
category = IneffectualAdditionCategory.NEWPROSPREDUNDANCY;
if(justMap.containsKey(exp)) {
Set<IneffectualAdditionCategory> catSet = (Set<IneffectualAdditionCategory>) justMap.get(exp);
catSet.add(category);
justMap.put(exp, catSet);
}
else
justMap.put(exp, new HashSet<IneffectualAdditionCategory>(Collections.singleton(category)));
}
/**
* Update ineffectual removal justification map with a new category
* @param justMap Justification map
* @param exp Justification
* @param cat New category
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private void updateJustificationMapWithRemoval(HashMap justMap, Explanation<OWLAxiom> exp, String cat) {
IneffectualRemovalCategory category = null;
if(cat.equalsIgnoreCase("redundant"))
category = IneffectualRemovalCategory.REDUNDANCY;
else if(cat.equalsIgnoreCase("rewrite"))
category = IneffectualRemovalCategory.REWRITE;
else if(cat.equalsIgnoreCase("prewrite"))
category = IneffectualRemovalCategory.PREWRITE;
else if(cat.equalsIgnoreCase("reshuffle"))
category = IneffectualRemovalCategory.RESHUFFLEREDUNDANCY;
else if(cat.equalsIgnoreCase("new"))
category = IneffectualRemovalCategory.NEWPROSPREDUNDANCY;
if(justMap.containsKey(exp)) {
Set<IneffectualRemovalCategory> set = (Set<IneffectualRemovalCategory>) justMap.get(exp);
set.add(category);
justMap.put(exp, set);
}
else
justMap.put(exp, new HashSet<IneffectualRemovalCategory>(Collections.singleton(category)));
}
/**
* Determine if ontologies are logically equivalent
* @return true if ontologies are logically equivalent, false otherwise
*/
public boolean isEquivalent() {
if(logicalChangeSet == null) logicalChangeSet = new LogicalDiff(ont1, ont2, verbose).getDiff();
if(logicalChangeSet.getEffectualAdditionAxioms().isEmpty() && logicalChangeSet.getEffectualRemovalAxioms().isEmpty())
return true;
else
return false;
}
/**
* Create an empty ontology reasoner, where the empty ontology contains entity declarations
* for all entities in sig(O1) and sig(O2)
* @return Empty ontology reasoner
*/
private OWLReasoner createEmptyOntologyReasoner() {
OWLOntology emptyOnt = null;
try { emptyOnt = man.createOntology(); }
catch (OWLOntologyCreationException e) { e.printStackTrace(); }
return new ReasonerLoader(emptyOnt).createReasoner(false);
}
/**
* Check if the signatures of 2 axioms overlap
* @param ax1 Axiom 1
* @param ax2 Axiom 2
* @return true if the signatures overlap, false otherwise
*/
private boolean signatureOverlaps(OWLAxiom ax1, OWLAxiom ax2) {
boolean overlaps = false;
for(OWLEntity e : ax1.getSignature()) {
if(ax2.getSignature().contains(e))
overlaps = true;
}
return overlaps;
}
/**
* Get signature union of both ontologies
* @return Signature union of both ontologies
*/
private Set<OWLEntity> getSignatureUnion() {
Set<OWLEntity> sig = new HashSet<OWLEntity>();
sig.addAll(ont1.getSignature());
sig.addAll(ont2.getSignature());
return sig;
}
/**
* Get the set of declarations for the signature union
* @return Set of declarations for the signature union
*/
@SuppressWarnings("unused")
private Set<OWLAxiom> getSignatureDeclarations() {
OWLDataFactory df = man.getOWLDataFactory();
Set<OWLAxiom> result = new HashSet<OWLAxiom>();
for(OWLEntity e : getSignatureUnion()) {
result.add(df.getOWLDeclarationAxiom(e));
}
return result;
}
/**
* Create a new ontology containing the specified axiom and all entity declarations
* @param axiom Axiom
* @return OWL ontology
*/
private OWLOntology createOntology(OWLAxiom axiom) {
OWLOntology ont = null;
try { ont = man.createOntology(Collections.singleton(axiom)); }
catch (OWLOntologyCreationException e) { e.printStackTrace(); }
return ont;
}
/**
* Get the structural change set between given ontologies
* @return Structural change set
*/
public StructuralChangeSet getStructuralChangeSet() {
return structuralChangeSet;
}
/**
* Get Manchester syntax of an OWL object
* @param obj OWL object
* @return A string with the object's conversion to Manchester syntax
*/
public static String getManchesterRendering(OWLObject obj) {
StringWriter wr = new StringWriter();
ManchesterOWLSyntaxObjectRenderer render = new ManchesterOWLSyntaxObjectRenderer(wr, p);
obj.accept(render);
String str = wr.getBuffer().toString();
str = str.replace("<", "");
str = str.replace(">", "");
return str;
}
/**
* Print diff results
*/
public void printDiff() {
System.out.println(" Categorised axiom changes:" +
"\n\tEffectual Additions: " + categorisedChangeSet.getEffectualAdditions().size() +
"\n\t Strengthenings: " + categorisedChangeSet.getStrengthenings().size() +
"\n\t Strengthenings w/ New Terms: " + categorisedChangeSet.getStrengtheningsWithNewTerms().size() +
"\n\t New Descriptions: " + categorisedChangeSet.getNewDescriptions().size() +
"\n\t Modified Definitions: " + categorisedChangeSet.getAddedModifiedDefinitions().size() +
"\n\t Modified Definitions w/ New Terms: " + categorisedChangeSet.getAddedModifiedDefinitionsWithNewTerms().size() +
"\n\t Pure Additions: " + categorisedChangeSet.getPureAdditions().size() +
"\n\t Pure Additions w/ New Terms: " + categorisedChangeSet.getPureAdditionsWithNewTerms().size() +
"\n\tEffectual Removals: " + categorisedChangeSet.getEffectualRemovals().size() +
"\n\t Weakenings: " + categorisedChangeSet.getWeakenings().size() +
"\n\t Weakenings w/ Retired Terms: " + categorisedChangeSet.getWeakeningsWithRetiredTerms().size() +
"\n\t Retired Descriptions: " + categorisedChangeSet.getRetiredDescriptions().size() +
"\n\t Modified Definitions: " + categorisedChangeSet.getRemovedModifiedDefinitions().size() +
"\n\t Modified Definitions w/ Retired Terms: " + categorisedChangeSet.getRemovedModifiedDefinitionsWithRetiredTerms().size() +
"\n\t Pure Removals: " + categorisedChangeSet.getPureRemovals().size() +
"\n\t Pure Removals w/ Retired Terms: " + categorisedChangeSet.getPureRemovalsWithRetiredTerms().size() +
"\n\tIneffectual Additions: " + categorisedChangeSet.getIneffectualAdditions().size() +
"\n\t Rewrites: " + (categorisedChangeSet.getAddedRewrites().size()+categorisedChangeSet.getAddedPartialRewrites().size()) +
"\n\t Complete Rewrites: " + categorisedChangeSet.getAddedRewrites().size() +
"\n\t Partial Rewrites: " + categorisedChangeSet.getAddedPartialRewrites().size() +
"\n\t Redundancies: " + categorisedChangeSet.getAddedRedundancies().size() +
"\n\t Prospective Redundancies: " + categorisedChangeSet.getAddedProspectiveRedundancies().size() +
"\n\t Reshuffle Redundancies: " + categorisedChangeSet.getAddedReshuffleRedundancies().size() +
"\n\t New Redundancies: " + categorisedChangeSet.getAddedProspectiveNewRedundancies().size() +
"\n\tIneffectual Removals: " + categorisedChangeSet.getIneffectualRemovals().size() +
"\n\t Rewrites: " + (categorisedChangeSet.getRemovedRewrites().size()+categorisedChangeSet.getRemovedPartialRewrites().size()) +
"\n\t Complete Rewrites: " + categorisedChangeSet.getRemovedRewrites().size() +
"\n\t Partial Rewrites: " + categorisedChangeSet.getRemovedPartialRewrites().size() +
"\n\t Redundancies: " + categorisedChangeSet.getRemovedRedundancies().size() +
"\n\t Prospective Redundancies: " + categorisedChangeSet.getRemovedProspectiveRedundancies().size() +
"\n\t Reshuffle Redundancies: " + categorisedChangeSet.getRemovedReshuffleRedundancies().size() +
"\n\t New Redundancies: " + categorisedChangeSet.getRemovedProspectiveNewRedundancies().size() + "\n");
}
/**
* Get an XML change report for the change set computed by this diff
* @return XML change report object
*/
public XMLAxiomDiffReport getXMLReport() {
if(categorisedChangeSet == null) categorisedChangeSet = getDiff();
return new XMLAxiomDiffReport(ont1, ont2, categorisedChangeSet);
}
/**
* Get a CSV change report
* @return Change report as a CSV document
*/
public String getCSVChangeReport() {
if(categorisedChangeSet == null) categorisedChangeSet = getDiff();
return new CSVAxiomDiffReport().getReport(categorisedChangeSet);
}
/**
* Dispose and nullify reasoner instance
* @param reasoner OWL reasoner
*/
private void cleanUp(OWLReasoner reasoner) {
reasoner.dispose(); reasoner = null;
}
/**
* Remove and nullify ontology instance
* @param ont OWL ontology
*/
private void cleanUp(OWLOntology ont) {
OWLOntologyManager man = ont.getOWLOntologyManager();
man.removeOntology(ont); ont = null;
}
/**
* Empty and nullify map of objects
* @param s Map of objects
*/
private void cleanUp(Map<?,?> s) {
s.clear(); s = null;
}
}